package com.gitee.wsl.jvm.task.thread

import timber.log.Timber

/**
 * An abstract base class for threads which support pausing and resuming.
 */
abstract class PausableThread : Thread() {
    /**
     * @return true if this thread is currently pausing, false otherwise.
     */
    @get:Synchronized
    var isPausing = true
        private set
    private var mRunning = true

    @get:Synchronized
    var isCanceled = false
        private set

    private var mShouldStop = false

    /**
     * Causes the current thread to wait until this thread is pausing.
     */
    fun awaitPausing() {
        synchronized(this) {
            while (!isPausing) {
                if (dbg) Timber.d(
                    "Await Pause {}",
                    threadName
                )
                try {
                    (this as Object).wait(100)
                } catch (e: InterruptedException) {
                    /* restore the interrupted status */
                    interrupt()
                }
            }
        }
    }

    @Synchronized
    fun finish() {
        if (!mRunning) return
        Timber.d("Finish {}", threadName)
        mShouldStop = true
        interrupt()
    }

    /**
     * The thread should stop its work temporarily.
     */
    @Synchronized
    fun pause() {
        if (!isCanceled) {
            isCanceled = true
            interrupt()
        }
    }

    /**
     * The paused thread should continue with its work.
     */
    @Synchronized
    fun proceed() {
        if (isCanceled) {
            isCanceled = false
            (this as Object).notify()
        }
    }

    override fun run() {
        mRunning = true
        setName(threadName)
        setPriority(threadPriority)
        synchronized(this) {
        while (!mShouldStop) {
                if (mShouldStop) break
                while (isCanceled || !hasWork()) {
                    try {
                        if (isCanceled) {
                            isPausing = true
                            if (dbg) Timber.d(
                                "Pausing: {}",
                                threadName
                            )
                        }
                        (this as Object).wait()
                    } catch (e: InterruptedException) {
                        if (dbg) Timber.d(
                            "Interrupted {} {}:{}",
                            threadName,
                            isCanceled,
                            mShouldStop
                        )
                        if (mShouldStop) break
                    }
                }
                if (isPausing) {
                    isPausing = false
                    afterPause()
                }
            }
            try {
                doWork()
            } catch (e: InterruptedException) {
                if (dbg) Timber.d(
                    "Interrupted {} {}:{}",
                    threadName,
                    isCanceled,
                    mShouldStop
                )
            }
        }
        Timber.d("Done {}", threadName)
        isPausing = true
        mRunning = false
        afterRun()
    }

    /**
     * Called once when this thread continues to work after a pause. The default
     * implementation is empty.
     */
    protected fun afterPause() {
        // do nothing
    }

    /**
     * Called once at the end of the [.run] method. The default
     * implementation is empty.
     */
    protected fun afterRun() {
        // do nothing
    }

    /**
     * Called when this thread is not paused and should do its work.
     *
     * @throws InterruptedException
     * if the thread has been interrupted.
     */
    @Throws(InterruptedException::class)
    protected abstract fun doWork()

    protected abstract val threadName: String
    protected val threadPriority: Int
        /**
         * @return the priority of this thread. The default value is
         * [Thread.NORM_PRIORITY].
         */
        protected get() = NORM_PRIORITY

    /**
     * @return true if this thread has some work to do, false otherwise.
     */
    protected abstract fun hasWork(): Boolean

    companion object {
        //private val log: Logger = LoggerFactory.getLogger(PausableThread::class.java)
        private const val dbg = false
    }
}

